/**
 * Copyright © 2016-2025 The Thingsboard Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.terracloud.server.controller;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import com.terracloud.server.common.data.device.DeviceAccessControlMode;
import com.terracloud.server.common.data.device.DeviceAccessControlRule;
import com.terracloud.server.common.data.exception.ThingsboardErrorCode;
import com.terracloud.server.common.data.exception.ThingsboardException;
import com.terracloud.server.common.data.id.DeviceAccessControlRuleId;
import com.terracloud.server.common.data.id.TenantId;
import com.terracloud.server.common.data.page.PageData;
import com.terracloud.server.common.data.page.PageLink;
import com.terracloud.server.config.annotations.ApiOperation;
import com.terracloud.server.queue.util.TbCoreComponent;
import com.terracloud.server.service.device.DeviceAccessControlService;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import static com.terracloud.server.controller.ControllerConstants.*;

@RestController
@TbCoreComponent
@RequestMapping("/api")
@RequiredArgsConstructor
@Slf4j
public class DeviceAccessControlController extends BaseController {

    private final DeviceAccessControlService deviceAccessControlService;

    @ApiOperation(value = "保存设备接入控制规则 (Save Device Access Control Rule)",
            notes = "创建或更新设备接入控制规则。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "设备接入控制规则已成功保存",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = DeviceAccessControlRule.class))),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/deviceAccess/rule", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> saveDeviceAccessControlRule(@RequestBody DeviceAccessControlRule rule) throws ThingsboardException {
        TenantId tenantId = getCurrentUser().getTenantId();
        ListenableFuture<DeviceAccessControlRule> future = toListenableFuture(() -> deviceAccessControlService.saveDeviceAccessControlRule(tenantId, rule));
        return handleDeviceAccessControlRuleFuture(future);
    }

    @ApiOperation(value = "更新设备接入控制规则 (Update Device Access Control Rule)",
            notes = "更新设备接入控制规则。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "设备接入控制规则已成功更新",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = DeviceAccessControlRule.class))),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "404", description = "设备接入控制规则未找到"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/deviceAccess/rule/{ruleId}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> updateDeviceAccessControlRule(@PathVariable("ruleId") String strRuleId,
                                                                          @RequestBody DeviceAccessControlRule rule) throws ThingsboardException {
        checkParameter("ruleId", strRuleId);
        DeviceAccessControlRuleId ruleId = new DeviceAccessControlRuleId(toUUID(strRuleId));
        rule.setId(ruleId);
        TenantId tenantId = getCurrentUser().getTenantId();
        ListenableFuture<DeviceAccessControlRule> future = toListenableFuture(() -> deviceAccessControlService.saveDeviceAccessControlRule(tenantId, rule));
        return handleDeviceAccessControlRuleFuture(future);
    }

    @ApiOperation(value = "获取设备接入控制规则 (Get Device Access Control Rule)",
            notes = "根据规则ID获取设备接入控制规则。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "设备接入控制规则",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = DeviceAccessControlRule.class))),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "404", description = "设备接入控制规则未找到"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/deviceAccess/rule/{ruleId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> getDeviceAccessControlRuleById(@PathVariable("ruleId") String strRuleId) throws ThingsboardException {
        checkParameter("ruleId", strRuleId);
        DeviceAccessControlRuleId ruleId = new DeviceAccessControlRuleId(toUUID(strRuleId));
        TenantId tenantId = getCurrentUser().getTenantId();
        ListenableFuture<DeviceAccessControlRule> future = toListenableFuture(() -> deviceAccessControlService.findDeviceAccessControlRuleById(tenantId, ruleId));
        return handleDeviceAccessControlRuleFuture(future);
    }

    @ApiOperation(value = "删除设备接入控制规则 (Delete Device Access Control Rule)",
            notes = "根据规则ID删除设备接入控制规则。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "设备接入控制规则已成功删除"),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "404", description = "设备接入控制规则未找到"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/deviceAccess/rule/{ruleId}", method = RequestMethod.DELETE)
    @ResponseStatus(value = HttpStatus.OK)
    public DeferredResult<ResponseEntity<?>> deleteDeviceAccessControlRule(@PathVariable("ruleId") String strRuleId) throws ThingsboardException {
        checkParameter("ruleId", strRuleId);
        DeviceAccessControlRuleId ruleId = new DeviceAccessControlRuleId(toUUID(strRuleId));
        TenantId tenantId = getCurrentUser().getTenantId();
        ListenableFuture<Void> future = toListenableFuture(() -> {
            deviceAccessControlService.deleteDeviceAccessControlRule(tenantId, ruleId);
            return null;
        });
        return handleVoidFuture(future);
    }

    @ApiOperation(value = "获取设备接入控制规则列表 (Get Device Access Control Rules)",
            notes = "获取所有符合条件的设备接入控制规则列表。" + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "设备接入控制规则分页数据",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = PageData.class))),
            @ApiResponse(responseCode = "400", description = "请求参数无效"),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/deviceAccess/rules", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> getDeviceAccessControlRules(
            @RequestParam int pageSize,
            @RequestParam int page,
            @RequestParam(required = false) String textSearch,
            @RequestParam(required = false) String sortProperty,
            @RequestParam(required = false) String sortOrder,
            @RequestParam(required = false) String type,
            @RequestParam(required = false) String mode) throws ThingsboardException {
        // 不再使用当前租户ID，查询所有符合条件的规则
        PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
        ListenableFuture<PageData<DeviceAccessControlRule>> future;
        
        if (type != null && mode != null) {
            // 验证mode参数
            try {
                DeviceAccessControlMode modeEnum = DeviceAccessControlMode.valueOf(mode.toUpperCase());
                if (textSearch != null && !textSearch.isEmpty()) {
                    future = toListenableFuture(() -> deviceAccessControlService.findDeviceAccessControlRulesByTypeAndModeAndTextSearch(type, modeEnum, textSearch, pageLink));
                } else {
                    future = toListenableFuture(() -> deviceAccessControlService.findDeviceAccessControlRulesByTypeAndMode(type, modeEnum, pageLink));
                }
            } catch (IllegalArgumentException e) {
                throw new ThingsboardException("Invalid mode parameter. Valid values are: BLACKLIST, WHITELIST", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
            }
        } else if (type != null) {
            if (textSearch != null && !textSearch.isEmpty()) {
                future = toListenableFuture(() -> deviceAccessControlService.findDeviceAccessControlRulesByTypeAndTextSearch(type, textSearch, pageLink));
            } else {
                future = toListenableFuture(() -> deviceAccessControlService.findDeviceAccessControlRulesByType(type, pageLink));
            }
        } else if (mode != null) {
            // 验证mode参数
            try {
                DeviceAccessControlMode modeEnum = DeviceAccessControlMode.valueOf(mode.toUpperCase());
                if (textSearch != null && !textSearch.isEmpty()) {
                    future = toListenableFuture(() -> deviceAccessControlService.findDeviceAccessControlRulesByModeAndTextSearch(modeEnum, textSearch, pageLink));
                } else {
                    future = toListenableFuture(() -> deviceAccessControlService.findDeviceAccessControlRulesByMode(modeEnum, pageLink));
                }
            } catch (IllegalArgumentException e) {
                throw new ThingsboardException("Invalid mode parameter. Valid values are: BLACKLIST, WHITELIST", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
            }
        } else {
            if (textSearch != null && !textSearch.isEmpty()) {
                future = toListenableFuture(() -> deviceAccessControlService.findDeviceAccessControlRulesByTextSearch(textSearch, pageLink));
            } else {
                future = toListenableFuture(() -> deviceAccessControlService.findAllDeviceAccessControlRules(pageLink));
            }
        }
        
        return handlePageDataFuture(future);
    }

    @ApiOperation(value = "获取设备接入控制模式 (Get Device Access Control Mode)",
            notes = "获取当前租户的设备接入控制模式。" + TENANT_AUTHORITY_PARAGRAPH)
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "设备接入控制模式",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = DeviceAccessControlMode.class))),
            @ApiResponse(responseCode = "401", description = "用户未通过认证"),
            @ApiResponse(responseCode = "403", description = "用户无权限执行该操作"),
            @ApiResponse(responseCode = "500", description = "服务器内部错误")})
    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
    @RequestMapping(value = "/deviceAccess/mode", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DeferredResult<ResponseEntity<?>> getDeviceAccessControlMode() throws ThingsboardException {
        TenantId tenantId = getCurrentUser().getTenantId();
        ListenableFuture<DeviceAccessControlMode> future = toListenableFuture(() -> deviceAccessControlService.getAccessControlMode(tenantId));
        return handleDeviceAccessControlModeFuture(future);
    }

    private <T> ListenableFuture<T> toListenableFuture(SupplierWithException<T> supplier) {
        SettableFuture<T> future = SettableFuture.create();
        try {
            T result = supplier.get();
            future.set(result);
        } catch (Exception e) {
            future.setException(e);
        }
        return future;
    }

    private DeferredResult<ResponseEntity<?>> handleDeviceAccessControlRuleFuture(ListenableFuture<DeviceAccessControlRule> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<>() {
            @Override
            public void onSuccess(DeviceAccessControlRule result) {
                deferredResult.setResult(ResponseEntity.ok(result));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    private DeferredResult<ResponseEntity<?>> handleVoidFuture(ListenableFuture<Void> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<Void>() {
            @Override
            public void onSuccess(Void result) {
                deferredResult.setResult(ResponseEntity.ok(HttpStatus.OK));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    private DeferredResult<ResponseEntity<?>> handleDeviceAccessControlModeFuture(ListenableFuture<DeviceAccessControlMode> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<DeviceAccessControlMode>() {
            @Override
            public void onSuccess(DeviceAccessControlMode result) {
                // 如果未设置，默认为黑名单模式
                if (result == null) {
                    deferredResult.setResult(ResponseEntity.ok(DeviceAccessControlMode.BLACKLIST));
                } else {
                    deferredResult.setResult(ResponseEntity.ok(result));
                }
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    private DeferredResult<ResponseEntity<?>> handlePageDataFuture(ListenableFuture<PageData<DeviceAccessControlRule>> future) {
        DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
        Futures.addCallback(future, new FutureCallback<PageData<DeviceAccessControlRule>>() {
            @Override
            public void onSuccess(PageData<DeviceAccessControlRule> result) {
                deferredResult.setResult(ResponseEntity.ok(result));
            }

            @Override
            public void onFailure(@NotNull Throwable t) {
                deferredResult.setErrorResult(t);
            }
        }, MoreExecutors.directExecutor());
        return deferredResult;
    }

    // 辅助方法，用于在控制器中处理按类型和模式查询
    private PageData<DeviceAccessControlRule> findDeviceAccessControlRulesByTypeAndMode(TenantId tenantId, String type, String mode, PageLink pageLink) throws ThingsboardException {
        try {
            DeviceAccessControlMode modeEnum = DeviceAccessControlMode.valueOf(mode.toUpperCase());
            return deviceAccessControlService.findDeviceAccessControlRulesByTypeAndMode(tenantId, type, modeEnum, pageLink);
        } catch (IllegalArgumentException e) {
            throw new ThingsboardException("Invalid mode parameter. Valid values are: BLACKLIST, WHITELIST", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
        }
    }

    // 辅助方法，用于在控制器中处理按模式查询
    private PageData<DeviceAccessControlRule> findDeviceAccessControlRulesByMode(TenantId tenantId, String mode, PageLink pageLink) throws ThingsboardException {
        try {
            DeviceAccessControlMode modeEnum = DeviceAccessControlMode.valueOf(mode.toUpperCase());
            return deviceAccessControlService.findDeviceAccessControlRulesByMode(tenantId, modeEnum, pageLink);
        } catch (IllegalArgumentException e) {
            throw new ThingsboardException("Invalid mode parameter. Valid values are: BLACKLIST, WHITELIST", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
        }
    }

    @FunctionalInterface
    private interface SupplierWithException<T> {
        T get() throws Exception;
    }
}